const fs = require('fs');
const net = require('net');
const path = require('path');
const Client = require('ssh2').Client;

// 设置RemoteAgent的配置信息
// const sshConfig = {
//     host: 'label-anything.com',
//     port: 22,
//     username: 'work',
//     // identity_file: require('fs').readFileSync('/path/to/private/key'),
//     password: 'martin0905',
//     tunnel_src_port: 3000,
//     tunnel_dst_port: 3000
// };
// 定义RemoteAgent代理对象
class RemoteAgent {
    constructor(app, config) {
        this.app = app;
        this.config = config;
        this.ssh_client = new Client();
        this.local_server = null;
        this.active_sockets = null;
        this.log_prefix = '\t\t>> RemoteAgent ';
    }

    retryConnection() {
        console.log(`${this.log_prefix} retrying connection...`);
        setTimeout(() => this.connect(), 3000);
    }

    async connect() {
        console.log(`${this.log_prefix} connect...`);
        let config = this.config;
        return new Promise((resolve, reject) => {
            if (!this.ssh_client || !this.ssh_client.connected) {
                if (config.identity_file) {
                    try {
                        config.privateKey = fs.readFileSync(config.identity_file, 'utf8');
                    } catch (error) {
                        reject({ code: -1, msg: `Open private key file ${config.identity_file} error` });
                        return;
                    }
                }
                // console.log(config);

                this.ssh_client
                    .once('ready', () => {
                        console.log(`${this.log_prefix} SSH connection established.`);
                        this.ssh_client.connected = true;
                        resolve({ code: 0, msg: 'create ssh connnection ok!' });
                    })
                    .once('error', (error) => {
                        console.error(`${this.log_prefix} SSH connection error:`, error);
                        this.ssh_client.connected = false;

                        // 发送异步消息（消息仅处理一次）
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'connect_error',
                                error
                            }, false);

                        // 处理异常
                        if (error.code === 'ECONNRESET') {
                            this.retryConnection();
                        } else {
                            reject({ code: -1, msg: error.message });
                        }
                    })
                    .once('end', () => {
                        console.log(`${this.log_prefix} SSH connection ended.`);
                        this.ssh_client.connected = false;

                        // 发送异步消息
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'connect_end'
                            });

                        reject({ code: -1, msg: 'SSH connection ended' });
                    })
                    .connect(config);
            } else {
                console.warn(`${this.log_prefix} ssh_client has conntected!`);
                resolve({ code: 0, msg: 'ssh_client has connected!' });
            }
        });
    }

    async disconnect() {
        console.log(`${this.log_prefix} disconnecting`);
        const tasks = [];

        // 关闭所有打开的连接
        if (this.active_sockets) {
            this.active_sockets.forEach((socket) => {
                socket.destroy(); // 如果使用end，并不会直接关闭链接
            });
            this.active_sockets.clear();
        }

        // 关闭本地服务器
        if (this.local_server) {
            tasks.push(
                new Promise((resolve) => {
                    this.local_server.close((error) => {
                        if (error) {
                            console.error(`${this.log_prefix} Error closing local server:`, error);
                            reject(error);
                        } else {
                            console.log(`${this.log_prefix} Local server closed`);
                            resolve();
                        }
                    });
                    this.local_server = null;
                })
            );
        }

        // 关闭ssh client
        if (this.ssh_client && this.ssh_client.connected) {
            tasks.push(
                new Promise((resolve, reject) => {
                    this.ssh_client
                        .once('close', () => {
                            resolve();
                        })
                        .once('error', (error) => {
                            console.error(`${this.log_prefix} SSH Client Error:`, error);
                            reject(error);
                        });
                    this.ssh_client.end();
                })
            );
        }

        try {
            const results = await Promise.allSettled(tasks);
            const failure = [];
            results.forEach((result, idx) => {
                if (result.status === 'rejected') {
                    failure.push({ index: idx, reason: result.reason });
                }
            });

            if (failure.length > 0) {
                console.error(`${this.log_prefix} Some disconnect tasks failed:`, failure);
                return { code: -1, msg: failure };
            } else {
                console.log(`${this.log_prefix} Remote disconnect OK!`);
                this.ssh_client.removeAllListeners();
                return { code: 0, msg: 'Remote Disconnect OK!' };
            }
        } catch (error) {
            console.error(`${this.log_prefix} Error during disconnect:`, error);
            return { code: -2, msg: error.message };
        }
    }

    // 判断当前连接是否还存在(暂时只是返回状态)
    async isSSHConnected() {
        return this.ssh_client.connected;
    }

    async uploadFile(localPath, remotePath) {
        console.log(`${this.log_prefix} uploadFile localPath = `, localPath);

        return new Promise((resolve, reject) => {
            this.ssh_client.sftp((err, sftp) => {
                if (err) {
                    reject(null);
                    return;
                }

                const readStream = fs.createReadStream(localPath);
                const writeStream = sftp.createWriteStream(remotePath);

                writeStream.on('close', () => {
                    console.log(`${this.log_prefix} File successfully uploaded to ${remotePath}`);
                    readStream.close(); // 确保关闭readStream
                    sftp.end(); // 关闭SFTP会话
                    resolve(true);
                });

                writeStream.on('error', (error) => {
                    console.error(`${this.log_prefix} WriteStream Error: ${error}`);
                    readStream.close();
                    sftp.end();
                    reject(null);
                });

                readStream.on('error', (error) => {
                    console.error(`${this.log_prefix} ReadStream Error: ${error}`);
                    writeStream.end(); // 仅在书写错误时需手动关闭writeStream
                    sftp.end();
                    reject(null);
                });

                readStream.pipe(writeStream);
            });
        });
    }

    async downloadFile(remotePath, localPath) {
        return new Promise((resolve, reject) => {
            this.ssh_client.sftp((err, sftp) => {
                if (err) {
                    reject(err);
                    return;
                }

                const readStream = sftp.createReadStream(remotePath);
                const writeStream = fs.createWriteStream(localPath);

                readStream.on('error', (error) => {
                    console.error(`${this.log_prefix} ReadStream Error: ${error}`);
                    writeStream.end(); // 关闭writeStream
                    sftp.end(); // 关闭SFTP会话
                    reject(error);
                });

                writeStream.on('close', () => {
                    console.log(`${this.log_prefix} File successfully downloaded to ${localPath}`);
                    sftp.end(); // 关闭SFTP会话
                    resolve();
                });

                writeStream.on('error', (error) => {
                    console.error(`${this.log_prefix} WriteStream Error: ${error}`);
                    readStream.destroy(); // 销毁readStream
                    sftp.end(); // 关闭SFTP会话
                    reject(error);
                });

                readStream.pipe(writeStream);
            });
        });
    }

    async executeCommand(command) {
        console.log(`${this.log_prefix} executeCommand command = `, command);
        this.app &&
            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                event: 'cmdstart',
                data: {
                    command
                }
            });
        return new Promise((resolve, reject) => {
            this.ssh_client.exec(command, (err, stream) => {
                if (err) {
                    // reject({ code: -1, msg: err });
                    throw err;
                }

                let stdout = '';
                let stderr = '';
                stream
                    .on('data', (data) => {
                        // console.log(`\t>> [stdio]: ${data}`);
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'stdio',
                                data: data.toString()
                            });
                        stdout += data;
                    })
                    .on('close', (code, signal) => {
                        // console.log(`remoteAgent exec command = ${command} [code = ${code}]`);
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'cmdend',
                                data: {
                                    code,
                                    command
                                }
                            });
                        if (code === 0) {
                            resolve({ code, stdout });
                        } else {
                            reject({ code, stderr, stdout });
                        }
                    })
                    .stderr.on('data', (data) => {
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'stderr',
                                data: data.toString()
                            });
                        stderr += data;
                    });
            });
        });
    }

    // 执行远程脚本
    async execScript(script_path) {
        try {
            let result = await this.executeCommand(script_path);
            return result;
        } catch (error) {
            console.error(`${this.log_prefix} executing remote script:`, error);
            throw error;
        }
    }

    // 在远程执行本地脚本
    async execScriptFromLocal(script_path) {
        console.log(`${this.log_prefix} execScriptFromLocal script_path = `, script_path);
        try {
            // 读取本地脚本内容
            let result = null;

            // 定义远程命令
            const remoteTempDir = '$HOME/.labelapp/tmp/';
            const remoteScriptPath = `${remoteTempDir}${path.basename(script_path)}`;

            // 1. 创建远程临时目录
            await this.executeCommand(`mkdir -p ${remoteTempDir}`);

            // 2. 将脚本内容写入远程临时文件
            // const echoCommand = `echo "${scriptContent.replace(/"/g, '\\"')}" > ${remoteScriptPath}`;
            // await this.executeCommand(echoCommand);
            result = await this.executeCommand(`echo ${remoteScriptPath}`);

            await this.uploadFile(script_path, result.stdout.trim());

            // 3. 给临时脚本执行权限
            await this.executeCommand(`chmod +x ${remoteScriptPath}`);

            // 4. 执行远程临时文件
            result = await this.executeCommand(remoteScriptPath);

            // 5. 可选择性删除临时文件
            await this.executeCommand(`rm ${remoteScriptPath}`);

            return result;
        } catch (error) {
            console.error(`${this.log_prefix} executing script:`, error);
            throw error;
        }
    }

    async createTunnel(tunnel_info) {
        console.log(`${this.log_prefix} createTunnel tunnel_info = `, tunnel_info);
        return new Promise((resolve, reject) => {
            if (this.local_server) {
                console.warn(`${this.log_prefix} reate too many tunnels!`);
                reject({ code: -1, msg: 'Create too many tunnels!' });
                return;
            }

            // 活动连接的集合
            this.active_sockets = new Set();

            // 启动本地服务
            this.local_server = net.createServer((localSocket) => {
                // console.log('\t>> [RemoteAgent/createTunnel] Received local connection.');
                this.ssh_client.forwardOut('127.0.0.1', tunnel_info.src_port, '127.0.0.1', tunnel_info.dst_port, (err, stream) => {
                    if (err) {
                        console.error(`${this.log_prefix} ForwardOut Error: ` + err.toString());
                        localSocket.destroy();
                        reject({ code: -1, msg: err.toString() });
                        return;
                    }

                    // 保存本地链接和通道并打通新的数据流
                    localSocket.on('error', (error) => {
                        console.error(`${this.log_prefix} LocalSocket Error: ` + error.toString());
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'tunnel_socket_error',
                                error
                            });
                        stream.destroy();
                    });

                    localSocket.on('close', () => {
                        // console.log('\t>> [RemoteAgent/createTunnel] ## Local connection closed.');
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'tunnel_socket_close'
                            });
                        this.active_sockets.delete(localSocket);
                        stream.destroy();
                    });

                    stream.on('error', (error) => {
                        console.error(`${this.log_prefix} Stream Error:` + error.toString());
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'tunnel_stream_error',
                                error
                            });
                        localSocket.destroy();
                    });

                    stream.on('close', () => {
                        console.log(`${this.log_prefix} Remote tunnel closed.`);
                        this.app &&
                            this.app.queue.enqueue(`${this.config.id}:${this.config.name}`, {
                                event: 'tunnel_stream_close'
                            });
                        localSocket.destroy();
                    });

                    console.log(`${this.log_prefix} Remote tunnel established.`);
                    this.config.tunnel_src_port = tunnel_info.src_port;
                    this.config.tunnel_dst_port = tunnel_info.dst_port;
                    localSocket.pipe(stream).pipe(localSocket);
                    this.active_sockets.add(localSocket);
                });
            });

            // 监听监听器的 'error' 事件处理函数
            this.local_server.on('error', (err) => {
                console.error(`${this.log_prefix} Server Error:`, err);
                if (this.local_server) {
                    if (this.active_sockets) {
                        this.active_sockets.forEach((socket) => {
                            socket.destroy();
                        });
                        this.active_sockets.clear();
                    }
                    this.local_server.close();
                    this.local_server = null;
                }
                reject({ code: -1, msg: err.toString() });
            });

            // 监听监听器的 'listening' 事件处理函数
            this.local_server.on('listening', () => {
                console.log(`${this.log_prefix} Listening for connections on local port ${tunnel_info.src_port}`);
                resolve({ code: 0, msg: 'Remote Create Tunnel OK!' });
            });

            // 开始监听指定端口
            this.local_server.listen(tunnel_info.src_port);
        });
    }

    // 还未经过严格的测试
    async createReverseTunnel(remotePort, localHost, localPort) {
        return new Promise((resolve, reject) => {
            this.ssh_client.forwardIn('0.0.0.0', remotePort, (err) => {
                if (err) {
                    return reject({ code: -1, msg: `ForwardIn error: ${err}` });
                }
                console.log(`${this.log_prefix} ForwardIn established on remote port ${remotePort}`);

                this.ssh_client.on('tcp connection', (info, accept, reject) => {
                    console.log(`${this.log_prefix} Incoming connection from ${info.srcAddr}:${info.srcPort}`);

                    const stream = accept();

                    // 创建一个本地连接
                    const localSocket = net.connect(localPort, localHost, () => {
                        console.log(`${this.log_prefix} Connected to local ${localHost}:${localPort}`);
                        stream.pipe(localSocket).pipe(stream);
                    });

                    localSocket.on('error', (err) => {
                        console.error(`${this.log_prefix} Local socket error: ${err}`);
                        stream.end();
                    });

                    stream.on('error', (err) => {
                        console.error(`${this.log_prefix} Stream error: ${err}`);
                        localSocket.end();
                    });
                });

                resolve({ code: 0, msg: 'Reverse tunnel created' });
            });
        });
    }
}

module.exports = { RemoteAgent };
